/*
 * Copyright 2006 - Chris Maki
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.agoracms.model;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.ManyToMany;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.OrderBy;
import javax.persistence.Table;

import org.apache.commons.lang.builder.ToStringBuilder;

@Entity
@Table(name="user")
@NamedQueries( {
    @NamedQuery(name="user.userByName", query="select u from User u where u.username like ?1"),
    @NamedQuery(name="user.updatedAtIP", query="select u from User u where u.updatedAtIP like :ip"),
    @NamedQuery(name="user.updatedAtIP2", query="select u from User u where u.updatedAtIP like ?1"),
    @NamedQuery(name="user.pms", query="select u.messages from User u where u.updatedAtIP like :ip"),
    // users that started a post
    @NamedQuery(name="user.startedPosts", query="SELECT DISTINCT u FROM User u, Post p WHERE u = p.createdByUser AND p.parent IS NULL")
} )
@NamedQuery(name="user.byName", query="select u from User u where u.username like ?1")
public class User extends ModelBase {

   @Column(name="username")
    private String username;
    
    @Column(length=32, name="password")
    private String password;

    
    @Embedded
    @AttributeOverrides( { 
        @AttributeOverride(name="firstName", column=@Column(name="first_name")),
        @AttributeOverride(name="lastName", column=@Column(name="last_name")) } )
    private UserDetail userDetail = new UserDetail();
    

    @OneToMany(mappedBy="toUser", cascade=CascadeType.ALL)
    @OrderBy("number DESC")
    private List<PrivateMessage> messages = new ArrayList<PrivateMessage>();
    
    @OneToMany(mappedBy="fromUser", cascade=CascadeType.ALL)
    @OrderBy("number DESC")
    private List<PrivateMessage> messagesSent = new ArrayList<PrivateMessage>();

    @ManyToMany(mappedBy="users", cascade={CascadeType.PERSIST, CascadeType.REMOVE})
    private Set<Role> roles = new HashSet<Role>();
    
    // If there are no users for this relationship, remove all UserIpAddress
    // instances, this is a composition type relationship
    @OneToMany(cascade=CascadeType.ALL, mappedBy="user")
    protected Set<UserIpAddress> userIPAddresses = new HashSet<UserIpAddress>();
    
    // If there are no users for this relationship, remove all Address
    // instances, this is a composition type relationship
    // TopLinkFix
    // @OneToOne(cascade=CascadeType.ALL, mappedBy="user", fetch=FetchType.LAZY)
    @OneToOne(cascade=CascadeType.ALL, mappedBy="user")
    private Address address = new Address();

    private static final long serialVersionUID = -7966330655353574056L;


    public UserDetail getUserDetail() {
        return userDetail;
    }

    public void setUserDetail(UserDetail detail) {
        this.userDetail = detail;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String userName) {
        this.username = userName;
    }

    public List<PrivateMessage> getMessages() {
        return messages;
    }

    public void setMessages(List<PrivateMessage> messages) {
        this.messages = messages;
    }

    public List<PrivateMessage> getMessagesSent() {
		return messagesSent;
	}

	public void setMessagesSent(List<PrivateMessage> messagesSent) {
		this.messagesSent = messagesSent;
	}

	/**
	 * Adds a PM to the list of PM's available for this User. This method does
	 * not manage the relationship, use the setToUser or setToFromUser methods
	 * on PrivateMessage for relationship management
	 * 
	 * @param pm The private message for this user
	 * @see PrivateMessage#setToFromUser(User, User)
	 * @see PrivateMessage#setToUser(User)
	 */
	public void addMessage(PrivateMessage pm) {
        messages.add(pm);
    }

	/**
	 * Adds a PM to the list of PM's sent by this User. This method does
	 * not manage the relationship, use the setFromUser or setToFromUser methods
	 * on PrivateMessage for relationship management
	 * 
	 * @param pm The private message this user sent
	 * @see PrivateMessage#setToFromUser(User, User)
	 * @see PrivateMessage#setFromUser(User)
	 */
    public void addSentMessage(PrivateMessage pm) {
    	messagesSent.add(pm);
    }
    public void removeMessage(PrivateMessage pm) {
        messages.remove(pm);
    }

    // TODO make sure add/remove/delegate methods for collection are implemented
    
    // TODO add toString, hashCode, and equals methods
    public Set<Role> getRoles() {
        return roles;
    }

    public void setRoles(Set<Role> roles) {
        this.roles = roles;
    }

    public boolean addRole(Role arg0) { 
        return roles.add(arg0);
    }

    public boolean removeRole(Role arg0) {
        return roles.remove(arg0);
    }

    public Set<UserIpAddress> getUserIPAddresses() {
        return userIPAddresses;
    }

    public void setUserIPAddresses(Set<UserIpAddress> userIPAddresses) {
        this.userIPAddresses = userIPAddresses;
    }

    public boolean addIpAddress(String o) {
        UserIpAddress addr = new UserIpAddress();
        addr.setIpAddress(o);
        boolean addSuccessful = userIPAddresses.add(addr);
        if (addSuccessful) {
        	// manage both sides of the relationship
        	addr.setUser(this);
        	
        }
        return addSuccessful;
    }

    public boolean removeIpAddress(String o) {
        UserIpAddress addr = new UserIpAddress();
        addr.setIpAddress(o);
        // remove the address from the set
        boolean result = userIPAddresses.remove(addr);
        
        return result;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
        address.setUser(this);
    }

	@Override
	public String toString() {
		return new ToStringBuilder(this)
		.append("user id: ", getId())
		.append("username : ", getUsername())
		.toString();
	}
    
}

